﻿
//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "OwnerPtr.h"
#include "ElementId.h"
#include "NdbObject.h"
#include "UniIdentity.h"
#include "DbDataSchema.h"
#include "IExternalObject.h"
#include "NdbObjectSchematic.h"
#include "IElementParentReporter.h"

#include "GRepModule.h"
#include <unordered_map>

using namespace gcmp;

namespace gcmp
{
    class IElement;
    class ICategory;
    class IDocument;
}

namespace Sample
{
    class CustomizerGStyleManager;
    class GraphicsStyleConfig;

    // 一个图元的显示样式重载的自定义数据，包括文档/视图两级下图元和图形节点的样式重载
    class ElementGStyleData : public gcmp::NdbObjectSchematic
    {
        DATA_BEGIN(Sample, ElementGStyleData, gcmp::NdbObjectSchematic, 8FE8BE23 - FE25 - 4F8F - 88D0 - 5F50A201A1F0, GRepModule)
            DATA(gcmp::ElementId, ElementGStyle)        // 类别级显示样式。如果嵌套在ViewLevelGNodeGStyleMap中，是视图级；否则是文档级
            UMAP_DATA(std::wstring, gcmp::ElementId, std::hash<std::wstring>, GNodeNameToGNodeGStyleMap)        // 图形节点按照样式名称重载。如果嵌套在ViewLevelGNodeGStyleMap中，是视图级；否则是文档级
            UMAP_DATA(gcmp::UniIdentity, gcmp::ElementId, gcmp::UniIdentityHash, GNodeCategoryToGNodeGStyleMap) // 图形节点按照类别重载。如果嵌套在ViewLevelGNodeGStyleMap中，是视图级；否则是文档级
            UMAP_DATA(gcmp::ElementId, OwnerPtr<Sample::ElementGStyleData>, gcmp::ElementIdHash, ViewLevelGNodeGStyleMap)   // 视图级的显示样式重载的自定义数据。
            DATA_END
    public:
        // 会返回当前文档的单例CustomizerGStyleManager，从而在ElementGStyleData改变时，NDB会在事务中自动处理
        static gcmp::OwnerPtr<ElementGStyleData> Create();
        virtual gcmp::NdbObject* GetTopOwnerObject() const;
    public:
        const std::unordered_map<gcmp::ElementId, OwnerPtr<Sample::ElementGStyleData>, gcmp::ElementIdHash>& GetViewLevelGNodeGStyleMap() const { return GetViewLevelGNodeGStyleMap__(); };
        std::unordered_map<gcmp::ElementId, OwnerPtr<Sample::ElementGStyleData>, gcmp::ElementIdHash>& GetViewLevelGNodeGStyleMapFW() { return GetViewLevelGNodeGStyleMapFW__(); };

        // 获得图元级重载显示样式
        bool GetElementGStyle(gcmp::ElementId& gStyleId) const;
        // 获得视图图元级重载显示样式（调用嵌套的ViewLevelGNodeGStyleMap）
        bool GetViewElementGStyle(gcmp::ElementId viewId, gcmp::ElementId& gStyleId) const;
        // 获得图元中按照样式名称重载的显示样式
        bool GetGNodeNameGStyle(std::wstring gNodeName, gcmp::ElementId& gStyleId) const;
        // 获得图元中按照样式类别重载的显示样式
        bool GetGNodeCategoryGStyle(gcmp::UniIdentity gNodeCategory, gcmp::ElementId& gStyleId) const;
        // 获得视图级图元中按照样式名称重载的显示样式（调用嵌套的ViewLevelGNodeGStyleMap）
        bool GetViewGNodeNameGStyle(gcmp::ElementId viewId, std::wstring gNodeName, gcmp::ElementId& gStyleId) const;
        // 获得视图级图元中按照样式类别重载的显示样式（调用嵌套的ViewLevelGNodeGStyleMap）
        bool GetViewGNodeCategoryGStyle(gcmp::ElementId viewId, gcmp::UniIdentity gNodeCategory, gcmp::ElementId& gStyleId) const;

        // 设置图元级重载显示样式
        void UpdateElementGStyle(gcmp::ElementId& gStyleId);
        // 设置视图图元级重载显示样式（调用嵌套的ViewLevelGNodeGStyleMap）
        void UpdateViewElementGStyle(gcmp::ElementId viewId, gcmp::ElementId& gStyleId);
        // 设置图元中按照样式名称重载的显示样式
        void UpdateGNodeNameGStyle(std::wstring gNodeName, gcmp::ElementId& gStyleId);
        // 设置图元中按照样式类别重载的显示样式
        void UpdateGNodeCategoryGStyle(gcmp::UniIdentity gNodeCategory, gcmp::ElementId& gStyleId);
        // 设置视图级图元中按照样式名称重载的显示样式（调用嵌套的ViewLevelGNodeGStyleMap）
        void UpdateViewGNodeNameGStyle(gcmp::ElementId viewId, std::wstring gNodeName, gcmp::ElementId& gStyleId);
        // 设置视图级图元中按照样式类别重载的显示样式（调用嵌套的ViewLevelGNodeGStyleMap）
        void UpdateViewGNodeCategoryGStyle(gcmp::ElementId viewId, gcmp::UniIdentity gNodeCategory, gcmp::ElementId& gStyleId);

        // 获得该图元所有按照图形节点样式名称重载的显示样式（示例目前仅支持一个图形节点名称有一个重载样式，GDMP支持多个）
        const std::unordered_map<std::wstring, gcmp::ElementId, std::hash<std::wstring>>& GetGNodeNameGStyles() const;
        // 获得该图元所有按照图形节点样式类别重载的显示样式（示例目前仅支持一个图形节点名称有一个重载样式，GDMP支持多个）
        const std::unordered_map<gcmp::UniIdentity, gcmp::ElementId, gcmp::UniIdentityHash>& GetGNodeCategoryGStyles() const;
        // 获得该图元所有视图级按照图形节点样式名称重载的显示样式（示例目前仅支持一个图形节点名称有一个重载样式，GDMP支持多个）
        const std::unordered_map<std::wstring, gcmp::ElementId, std::hash<std::wstring>>& GetViewGNodeNameGStyles(gcmp::ElementId viewId) const;
        // 获得该图元所有视图级按照图形节点样式类别重载的显示样式（示例目前仅支持一个图形节点名称有一个重载样式，GDMP支持多个）
        const std::unordered_map<gcmp::UniIdentity, gcmp::ElementId, gcmp::UniIdentityHash>& GetViewGNodeCategoryGStyles(gcmp::ElementId viewId) const;
    private:
        // 获得视图对应该图元的视图显示样式自定义数据
        bool GetViewMap(gcmp::ElementId viewId, const ElementGStyleData*& pViewData) const;
        // 获得该图元对应一个视图的显示样式自定义数据，如果没有则创建一个并返回
        ElementGStyleData* GetOrAddViewMap(gcmp::ElementId viewId);
    };

    // 文档中所有图元对应显示样式重载数据的管理器，文档级全局唯一
    class CustomizerGStyleManager : public gcmp::NdbObjectSchematic, public gcmp::IExternalObject
    {
        CANNOT_COPY_OR_ASSIGN(CustomizerGStyleManager)
            DBOBJECT_DATA_BEGIN_AND_QUICK_CAST_FROM(Sample, CustomizerGStyleManager, gcmp::NdbObjectSchematic, 4345B1B8 - F205 - 411E - B565 - 677CAD5C9043, GRepModule, gcmp::IExternalObject)
            DATA_TRANSIENT(gcmp::IElement*, OwnerElement)
            UMAP_DATA(gcmp::ElementId, OwnerPtr<Sample::ElementGStyleData>, gcmp::ElementIdHash, ElementToElementGStyleDataMap)     // 图元到显示样式重载自定义数据的匹配
            DBOBJECT_DATA_END

    public:
        // 继承IExternalObject
        virtual bool SetOwnerElement(gcmp::IElement* pOwnerElement) override;
        virtual const gcmp::IElement* GetOwnerElement() const override { return GetOwnerElement__(); }
        virtual gcmp::IElement* GetOwnerElement() override { return GetOwnerElementFW__(); }
        gcmp::IDocument* GetDocument() const;
        gcmp::ElementId GetOwnerElementId() const;
        virtual void UpdateForWeakParentDeletion(const std::set<gcmp::ElementId>& deletedElementIds) override;
        virtual void ReportParents(gcmp::IElementParentReporter& reporter) const override;
        virtual bool FinishEditing() override { return false; };
        virtual NdbObject* GetTopOwnerObject() const override;
    public:
        // 获得或创建CustomizerGStyleManager单例，没有则创建
        static CustomizerGStyleManager* CreateOrGet(gcmp::IDocument* pDoc);
        // 获得CustomizerGStyleManager单例
        static CustomizerGStyleManager* Get(gcmp::IDocument* pDoc);

        // 初始化用于图形节点按照样式类别重载的类别
        void InitializeGStyleCategory();
        // 获得用于图形节点按照样式类别重载的类别
        std::map<std::wstring, const gcmp::ICategory*> GetGStyleCategories() const;
    public:
        // 获得文档级按图元重载的显示样式
        void GetElementGStyle(gcmp::ElementId elemId, gcmp::ElementId& gStyleId) const;
        // 获得视图级按图元重载的显示样式
        void GetViewElementGStyle(gcmp::ElementId elemId, gcmp::ElementId viewId, gcmp::ElementId& gStyleId) const;
        // 获得文档级按照图元的图形节点样式名称重载的显示样式
        void GetGNodeNameGStyle(gcmp::ElementId elemId, std::wstring gNodeName, gcmp::ElementId& gStyleId) const;
        // 获得文档级按照图元的图形节点样式类别重载的显示样式
        void GetGNodeCategoryGStyle(gcmp::ElementId elemId, gcmp::UniIdentity gNodeCategory, gcmp::ElementId& gStyleId) const;
        // 获得视图级按照图元的图形节点样式名称重载的显示样式
        void GetViewGNodeNameGStyle(gcmp::ElementId elemId, gcmp::ElementId viewId, std::wstring gNodeName, gcmp::ElementId& gStyleId) const;
        // 获得视图级按照图元的图形节点样式类别重载的显示样式
        void GetViewGNodeCategoryGStyle(gcmp::ElementId elemId, gcmp::ElementId viewId, gcmp::UniIdentity gNodeCategory, gcmp::ElementId& gStyleId) const;

        // 设置文档级按图元重载的显示样式
        void UpdateElementGStyle(gcmp::ElementId elemId, gcmp::ElementId& gStyleId);
        // 设置视图级按图元重载的显示样式
        void UpdateViewElementGStyle(gcmp::ElementId elemId, gcmp::ElementId viewId, gcmp::ElementId& gStyleId);
        // 设置文档级按照图元的图形节点样式名称重载的显示样式
        void UpdateGNodeNameGStyle(gcmp::ElementId elemId, std::wstring gNodeName, gcmp::ElementId& gStyleId);
        // 设置文档级按照图元的图形节点样式类别重载的显示样式
        void UpdateGNodeCategoryGStyle(gcmp::ElementId elemId, gcmp::UniIdentity gNodeCategory, gcmp::ElementId& gStyleId);
        // 设置视图级按照图元的图形节点样式名称重载的显示样式
        void UpdateViewGNodeNameGStyle(gcmp::ElementId elemId, gcmp::ElementId viewId, std::wstring gNodeName, gcmp::ElementId& gStyleId);
        // 设置视图级按照图元的图形节点样式类别重载的显示样式
        void UpdateViewGNodeCategoryGStyle(gcmp::ElementId elemId, gcmp::ElementId viewId, gcmp::UniIdentity gNodeCategory, gcmp::ElementId& gStyleId);

        // 获得图元按照图形节点样式名称重载的显示样式（示例目前仅支持一个图形节点名称有一个重载样式，GDMP支持多个）
        const std::unordered_map<std::wstring, gcmp::ElementId, std::hash<std::wstring>>& GetGNodeNameGStyles(gcmp::ElementId elemId) const;
        // 获得图元所有按照图形节点样式类别重载的显示样式（示例目前仅支持一个图形节点名称有一个重载样式，GDMP支持多个）
        const std::unordered_map<gcmp::UniIdentity, gcmp::ElementId, gcmp::UniIdentityHash>& GetGNodeCategoryGStyles(gcmp::ElementId elemId) const;
        // 获得图元所有视图级按照图形节点样式名称重载的显示样式（示例目前仅支持一个图形节点名称有一个重载样式，GDMP支持多个）
        const std::unordered_map<std::wstring, gcmp::ElementId, std::hash<std::wstring>>& GetViewGNodeNameGStyles(gcmp::ElementId elemId, gcmp::ElementId viewId) const;
        // 获得图元所有视图级按照图形节点样式类别重载的显示样式（示例目前仅支持一个图形节点名称有一个重载样式，GDMP支持多个）
        const std::unordered_map<gcmp::UniIdentity, gcmp::ElementId, gcmp::UniIdentityHash>& GetViewGNodeCategoryGStyles(gcmp::ElementId elemId, gcmp::ElementId viewId) const;

    public:
        // 用于图元的图形节点按照样式类别做显示样式重载的四个类别
        static const UniIdentity s_category_id_CategoryA;
        static const UniIdentity s_category_id_CategoryB;
        static const UniIdentity s_category_id_CategoryC;
        static const UniIdentity s_category_id_CategoryD;
    private:
        // 获得一个图元对应的显示样式重载自定义数据，没有则创建一个返回
        ElementGStyleData* GetOrAddElemMap(gcmp::ElementId elemId);
        // 获得一个图元对应的显示样式重载自定义数据
        bool GetElemMap(gcmp::ElementId elemId, const ElementGStyleData*& pElemGStyleData) const;
        gcmp::OwnerPtr<GraphicsStyleConfig> m_opGStyleConfig;
    };
}

